﻿using System;
using OpenTK;

namespace Lichtspiel.Input
{
    public class Keyboard : IUpdateable
    {
        private static int NumOfKeys = GetNumOfKeys();
        private bool[] state { get; set; }
        private bool[] lastState { get; set; }
        private bool[] internalState;

        public event EventHandler<KeyEventArgs> KeyPressed;
        public event EventHandler<KeyEventArgs> KeyReleased;

        public Keyboard(Game game) {
            game.window.Keyboard.KeyDown += Keyboard_KeyDown;
            game.window.Keyboard.KeyUp += Keyboard_KeyUp;

            state = new bool[NumOfKeys];
            lastState = new bool[NumOfKeys];
            internalState = new bool[NumOfKeys];
        }

        private void Keyboard_KeyUp(object sender, OpenTK.Input.KeyboardKeyEventArgs e) {
            internalState[(int)e.Key] = false;
        }

        private void Keyboard_KeyDown(object sender, OpenTK.Input.KeyboardKeyEventArgs e) {
            internalState[(int)e.Key] = true;
        }

        /// <summary>
        /// Is the Key currently down.
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public bool IsKeyDown(KeyCode key) {
            return this.state[(int) key];
        }

        /// <summary>
        /// Is the Key currently up.
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public bool IsKeyUp(KeyCode key) {
            return !this.state[(int)key];
        }

        /// <summary>
        /// Is the Key down this frame but not last frame.
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public bool IsKeyPressed(KeyCode key) {
            return this.state[(int)key] && !this.lastState[(int)key];
        }

        /// <summary>
        /// Was the Key down last frame but not this frame.
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public bool IsKeyReleased(KeyCode key) {
            return !this.state[(int)key] && this.lastState[(int)key];
        }

        public void Update(GameTime time) {
            state.CopyTo(lastState, 0);
            internalState.CopyTo(state, 0);

            for(int i = 0; i < state.Length; i++) {
                if (IsKeyPressed((KeyCode) i))
                    FireKeyPressedEvent(new KeyEventArgs((KeyCode) i));
                else if (IsKeyReleased((KeyCode) i))
                    FireKeyReleasedEvent(new KeyEventArgs((KeyCode)i));
            }
        }

        public void FireKeyPressedEvent(KeyEventArgs args) {
            if (KeyPressed != null) {
                KeyPressed(this, args);
            }
        }

        public void FireKeyReleasedEvent(KeyEventArgs args) {
            if (KeyReleased != null) {
                KeyReleased(this, args);
            }
        }

        /// <summary>
        /// Finds the max value of any key available
        /// </summary>
        /// <returns></returns>
        private static int GetNumOfKeys() {
            Array a = Enum.GetValues(typeof(KeyCode));
            int maxValue = 0;
            foreach (int i in a) {

                if (maxValue < i)
                    maxValue = i;
            }
            return maxValue;
        }
    }
}